use std::collections::BTreeSet;
use std::ffi::OsStr;
use std::fmt::Debug;
use std::hash::Hash;
use std::ops::Deref;
use std::path::PathBuf;
use std::process::Command;
use std::time::{Duration, Instant};

use crate::core::build_steps::{tool, dist};
use crate::flags::SubCommand;
use crate::{Build, Compiler};

#[derive(Clone, Copy)]
pub enum Kind {
    Build,
    Dist,
}

pub trait Step: 'static + Clone + Debug + PartialEq + Eq + Hash {
    /// `PathBuf` when directories are created or to return a `Compiler` once
    /// it's been assembled.
    type Output: Clone;

    /// Primary function to execute this rule. Can call `builder.ensure()`
    /// with other steps to run those.
    fn run(self, builder: &Builder<'_>) -> Self::Output;

    /// When bootstrap is passed a set of paths, this controls whether this rule
    /// will execute. However, it does not get called in a "default" context
    /// when we are not passed any paths; in that case, `make_run` is called
    /// directly.
    fn should_run(run: ShouldRun<'_>) -> ShouldRun<'_>;

    /// Builds up a "root" rule, either as a default rule or from a path passed
    /// to us.
    ///
    /// When path is `None`, we are executing in a context where no paths were
    /// passed. When `./x.py build` is run, for example, this rule could get
    /// called if it is in the correct list below with a path of `None`.
    fn make_run(_run: RunConfig<'_>) {
        // It is reasonable to not have an implementation of make_run for rules
        // who do not want to get called from the root context. This means that
        // they are likely dependencies (e.g., sysroot creation) or similar, and
        // as such calling them from ./x.py isn't logical.
        unimplemented!()
    }
}

pub struct ShouldRun<'a> {
    pub builder: &'a Builder<'a>,
    kind: Kind,
    paths: BTreeSet<PathBuf>,
}

impl<'a> ShouldRun<'a> {
    fn new(builder: &'a Builder<'_>, kind: Kind) -> ShouldRun<'a> {
        ShouldRun {
            builder,
            kind,
            paths: BTreeSet::new(),
        }
    }

    // single, non-aliased path
    pub fn path(self, path: &str) -> Self {
        self.paths(&[path])
    }

    /// Multiple aliases for the same job.
    ///
    /// This differs from [`path`] in that multiple calls to path will end up calling `make_run`
    /// multiple times, whereas a single call to `paths` will only ever generate a single call to
    /// `paths`.
    ///
    /// This is analogous to `all_krates`, although `all_krates` is gone now. Prefer [`path`] where possible.
    ///
    /// [`path`]: ShouldRun::path
    pub fn paths(mut self, paths: &[&str]) -> Self {
        self.paths.insert(
            paths
                .iter()
                .map(|p| PathBuf::from(p))
                .collect()
        );
        self
    }

    pub fn never(mut self) -> ShouldRun<'a> {
        // 这里可能会出错
        self.paths.insert(PathBuf::new());
        self
    }
}

pub struct RunConfig<'a> {
    pub builder: &'a Builder<'a>,
    // pub target: TargetSelection,
    pub paths: Vec<PathBuf>,
}

struct StepDescription {
    should_run: fn(ShouldRun<'_>) -> ShouldRun<'_>,
    make_run: fn(RunConfig<'_>),
    name: &'static str,
    kind: Kind,
}

impl StepDescription {
    fn from<S: Step>(kind: Kind) -> StepDescription {
        StepDescription {
            should_run: S::should_run,
            make_run: S::make_run,
            name: std::any::type_name::<S>(),
            kind
        }
    }

    fn maybe_run(&self, builder: &Builder<'_>, pathsets: Vec<PathBuf>) {
        if pathsets.is_empty() {
            return;
        }

        let run = RunConfig { builder, paths: pathsets.clone() };
        (self.make_run)(run);
    }

    fn run(v: &[StepDescription], builder: &Builder<'_>, paths: &[PathBuf]) {
        let should_runs = v
            .iter()
            .map(|desc| (desc.should_run)(ShouldRun::new(builder, desc.kind)))
            .collect::<Vec<_>>();

        // sanity checks on rules
        for (desc, should_run) in v.iter().zip(&should_runs) {
            assert!(
                !should_run.paths.is_empty(),
                "{:?} should have at least one pathset",
                desc.name 
            );
        }

        for (desc, should_run) in v.iter().zip(&should_runs) {
            desc.maybe_run(builder, should_run.paths.iter().cloned().collect());
        }
    }
}

pub struct Builder<'a> {
    pub build: &'a Build,
    pub kind: Kind,
    pub paths: Vec<PathBuf>,
}

impl<'a> Deref for Builder<'a> {
    type Target = Build;

    fn deref(&self) -> &Self::Target {
        self.build
    }
}

impl<'a> Builder<'a> {
    fn get_step_descriptions(kind: Kind) -> Vec<StepDescription> {
        macro_rules!  describe {
            ($($rule:ty),+ $(,)?) => {{
                vec![$(StepDescription::from::<$rule>(kind)),+]
            }};
        }
        match kind {
            Kind::Build => describe!(
                tool::RustDummy,
            ),
            Kind::Dist => describe!(
                dist::RustDummy,
            ),
        }
    }
    
    fn new_internal(build: &Build, kind: Kind, paths: Vec<PathBuf>) -> Builder<'_> {
        Builder {
            build,
            kind,
            paths,
        }
    }

    pub fn new(build: &Build) -> Builder<'_> {
        let paths = &build.config.paths;
        let (kind, paths) = match build.config.cmd {
            SubCommand::Build { .. }=> (Kind::Build, &paths[..]),
            SubCommand::Dist { .. }=> (Kind::Dist, &paths[..]),
        };
        
        Self::new_internal(build, kind, paths.to_owned())
    }

    pub fn execute_cli(&self) {
        self.run_step_descriptions(&Builder::get_step_descriptions(self.kind), &self.paths);
    }

    fn run_step_descriptions(&self, v: &[StepDescription], paths: &[PathBuf]) {
        StepDescription::run(v, self, paths);
    }

    fn bare_cargo(&self) -> Command {
        Command::new(&self.initial_cargo)
    }

    fn cargo(&self) -> Cargo {
        let mut cargo = self.bare_cargo();
        cargo
            .env("RUSTC_REAL", self.rustc());
        Cargo {
            command: cargo,
            allow_features: String::new(),
        }
    }

    fn rustc(&self) -> PathBuf {
        self.initial_rustc.clone()
    }

    /// Ensure that a given step is built, returning its output. This will
    /// cache the step, so it is safe (and good!) to call this as often as
    /// needed to ensure that all dependencies are built.
    pub fn ensure<S: Step>(&'a self, step: S) -> S::Output {
        // 很多skip掉了
        let out = step.clone().run(self);
        out
    }
}

#[derive(Debug)]
pub struct Cargo {
    command: Command,
    allow_features: String,
}

impl Cargo {
    pub fn new(
        builder: &Builder<'_>,
    ) -> Cargo {
        let cargo = builder.cargo();
        cargo
    }

    pub fn arg(&mut self, arg: impl AsRef<OsStr>) -> &mut Cargo {
        self.command.arg(arg.as_ref());
        self
    }
}

impl From<Cargo> for Command {
    fn from(cargo: Cargo) -> Command {
        cargo.command
    }
}